package max.cute.java8inaction.chap6;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;

import java.util.*;

import static java.util.stream.Collectors.*;

/**
 * 交易数据处理demo
 */
public class GroupingTransactions {

    public static List<Transaction> transactions = Arrays.asList(
            new Transaction(Currency.EUR, 1500.0,City.BeiJing),
            new Transaction(Currency.USD, 2300.0, City.BeiJing),
            new Transaction(Currency.GBP, 9900.0, City.TianJing),
            new Transaction(Currency.EUR, 1100.0, City.ShenZhen),
            new Transaction(Currency.JPY, 7800.0, City.ShangHai),
            new Transaction(Currency.CHF, 6700.0, City.BeiJing),
            new Transaction(Currency.EUR, 5600.0, City.TianJing),
            new Transaction(Currency.USD, 4500.0, City.ShenZhen),
            new Transaction(Currency.CHF, 3400.0, City.ShangHai),
            new Transaction(Currency.GBP, 3200.0, City.BeiJing),
            new Transaction(Currency.USD, 4600.0, City.ShangHai),
            new Transaction(Currency.JPY, 5700.0, City.ShangHai),
            new Transaction(Currency.EUR, 6800.0, City.ShangHai) );


    public static void main(String[] args) {
        groupTransactionAmount();
        System.out.println("- - - - - - ");
        partitioningTransaction();
        System.out.println("- - - - - - ");
        partitioningTransactionHavingCity();
        System.out.println("- - - - - - ");
    }

    /**
     * 对一个交易列表按货币分组，获得该货币的所有交易额总和(返回一个Map<Currency, Double>)。
     */
    private static void groupTransactionAmount() {
        // before java 8
        Map<Currency,Double> transactionAmountMap = new HashMap<>();

        for (Transaction transaction : transactions) {
            Currency currency = transaction.getCurrency();
            Double value = ObjectUtils.max(0d,transactionAmountMap.get(currency));
            transactionAmountMap.put(currency,value + transaction.getValue());
        }
        System.out.println(transactionAmountMap);

        // in java 8
        Map<Currency,Double> transactionAmountMapInJava8 = transactions.stream().collect(groupingBy(Transaction::getCurrency, summingDouble(Transaction::getValue)));
        System.out.println(transactionAmountMapInJava8);
    }

    /**
     * 将交易列表分成两组:贵的和不贵的(返回一个Map<Boolean, List<Transaction>>)。
     */
    private static void partitioningTransaction() {
        // before java 8
        final double avg = 3500d;
        Map<Boolean, List<Transaction>> partitioningTransactionMap = new HashMap<>();
        List<Transaction> trueList = new ArrayList<>();
        List<Transaction> falseList = new ArrayList<>();
        for (Transaction transaction : transactions) {
            if (transaction.getValue() > avg) {
                trueList.add(transaction);
            } else {
                falseList.add(transaction);
            }
        }
        partitioningTransactionMap.put(true,trueList);
        partitioningTransactionMap.put(false,falseList);
        System.out.println(partitioningTransactionMap);

        // in java 8
        Map<Boolean, List<Transaction>> partitioningTransactionMapInJava8 = transactions.stream().collect(partitioningBy(transaction -> transaction.getValue() > avg));
        System.out.println(partitioningTransactionMapInJava8);
    }

    /**
     * 创建多级分组，比如按城市对交易分组，
     * 然后进一步按照贵或不贵分组(返回一个 Map<City,Map<Boolean, List<Transaction>>>)。
     */
    private static void partitioningTransactionHavingCity() {
        // before java 8
        final double avg = 3500d;
        Map<City,List<Transaction>> cityListMap = new HashMap<>();
        for (Transaction transaction : transactions) {
            City city = transaction.getCity();
            List<Transaction> transactionList = cityListMap.get(city);
            if (transactionList == null) {
                transactionList = new ArrayList<>();
                transactionList.add(transaction);
                cityListMap.put(city,transactionList);
            } else {
                transactionList.add(transaction);
            }
        }
        Iterator<Map.Entry<City, List<Transaction>>> iterator = cityListMap.entrySet().iterator();
        Map<City,Map<Boolean, List<Transaction>>> cityPartitioningMap = new HashMap<>();
        while (iterator.hasNext()) {
            Map.Entry<City, List<Transaction>> entry = iterator.next();
            List<Transaction> innerTransactionList = entry.getValue();


            Map<Boolean, List<Transaction>> innerPartitioningMap = new HashMap<>();
            List<Transaction> trueList = new ArrayList<>();
            List<Transaction> falseList = new ArrayList<>();
            for (Transaction transaction : innerTransactionList) {
                if (transaction.getValue() > avg) {
                    trueList.add(transaction);
                } else {
                    falseList.add(transaction);
                }
            }
            innerPartitioningMap.put(true,trueList);
            innerPartitioningMap.put(false,falseList);
            cityPartitioningMap.put(entry.getKey(),innerPartitioningMap);
        }
        System.out.println(cityPartitioningMap);

        // in java 8
        Map<City,Map<Boolean, List<Transaction>>> cityPartitioningMapInJava8 = transactions.stream().collect(groupingBy(Transaction::getCity,partitioningBy(transaction -> transaction.getValue() > avg)));
        System.out.println(cityPartitioningMapInJava8);

    }

    @Getter
    @RequiredArgsConstructor
    public static class Transaction {
        private final Currency currency;
        private final double value;
        private final City city;

        @Override
        public String toString() {
            return "{" +
                    "currency=" + currency +
                    ", value=" + value +
                    ", city=" + city +
                    '}';
        }
    }

    public enum Currency {
        EUR, USD, JPY, GBP, CHF
    }

    public enum City {
        BeiJing, TianJing, ShenZhen, ShangHai
    }
}
